-- Copyright (c) 2025 Huawei Technologies Co., Ltd.
-- openUBMC is licensed under Mulan PSL v2.
-- You can use this software according to the terms and conditions of the Mulan PSL v2.
-- You may obtain a copy of Mulan PSL v2 at: http://license.coscl.org.cn/MulanPSL2
-- THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
-- EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
-- MERCHANTABILITY OR FIT FOR A PARTICULAR P

local lu = require 'luaunit'
local c_network_adapter = require 'device.class.network_adapter'
local c_object_manage = require 'mc.orm.object_manage'
local c_tasks = require ' mc.orm.tasks'
local c_network_port = require 'device.class.network_port'
local c_network_adapter_db = require 'network_adapter.db'
local hook_tasks = require 'test_common.hook_tasks'
local test_utils = require 'test_utils'
local create_mds_object = test_utils.create_mds_object
local c_optical_module = require 'device.class.optical_module'
local hardware_config = require 'hardware_config.JM800'
local skynet = require 'skynet'

TEST_oem_smbus_network_adapter = {}

local chip_data = {
    '\x80','\x12','\x1f','\x53','\xff','\xff','\xff','\xff','\xff','\x50',
    '\xd3','\x3b','\x01','\x7d','\x0d','\xff','\xff','\xff','\xff','\xff',
    '\xff','\xff','\xff','\xff','\xff','\xff','\xff','\xff','\xff','\xff',
    '\xff','\xff','\xff','\xff','\xff','\xff','\xff','\xff','\x03','\x00',
    '\x06','\x01','\x00','\x00','\xff','\xff','\x10','\xff','\xff','\xff',
    '\x2f','\xff','\xff','\x27','\x26','\xff','\xff','\xf0','\x10','\x11',
    '\x12','\x13','\xff','\xff','\x02','\xff','\x52','\x72','\xff','\xff',
    '\xff','\xff','\x00','\x20','\x40','\x60','\xff','\xff','\xff','\xff',
    '\x49','\x49','\x49','\x51','\xff','\xff','\xff','\xff','\x07','\x55',
    '\xff','\xff','\xff','\xff','\xff','\xff','\xff','\xff','\xff','\xff'
}

local chip = {
    WriteRead = true,
    Read = function(self, ctx, read_offset, read_len)
        local rsp_data = {}
        read_offset = read_offset + 1
        read_len = read_len - 1

        for i = 0, read_len do
            rsp_data[#rsp_data + 1] = chip_data[read_offset + i]
        end

        return table.concat(rsp_data)
    end,
    Write = function (self, ctx, write_offset, data)
        write_offset = write_offset + 1

        for i, v in ipairs(data) do
            chip_data[write_offset + i - 1] = string.char(v)
        end
        return true
    end
}

function TEST_oem_smbus_network_adapter:setUp()
    self.database = c_network_adapter_db(':memory:')

    hook_tasks.hook()
    self.object_manage = c_object_manage.new(self.database)
    self.object_manage.app = self

    self.position = '0001010104'
    self.uuid = 'testuid'
    self.port_count = 4

    --自发现增加网卡对象
    self.network_adapter_obj = test_utils.add_network_adapter(self.object_manage, 'NetworkAdapter_1', {
        BoardID = 32786,
        DeviceLocator = 'some locator',
        Name = "LingYao0517",
        Position = self.position,
        Model = "JM800",
        VendorID = "0x1f53",
        DeviceID = "0x2c20",
        NetworkPortCount = self.port_count
    })
    self.network_adapter_obj.RefChip = chip

    self.object_manage.mc:before_add_object(c_network_adapter.__class_name, self.network_adapter_obj)
    self.object_manage.mc:add_object(c_network_adapter.__class_name, self.network_adapter_obj,
        self.position)
    for id = 0, self.port_count - 1 do
        self:CreatePortAndOptModel(id)
    end

    self.object_manage.mc:prepare_ok()
    self:flush()
end

function TEST_oem_smbus_network_adapter:CreateVLANS()
end

function TEST_oem_smbus_network_adapter:CreatePortAndOptModel(id)
    -- 自发现添加 port 对象
    local mds_network_port = create_mds_object('NetworkPort_' .. (id + 1), 
        {PortID = id, NetDevFuncType = 1, UUID = self.uuid}, self.network_adapter_obj,
        '/bmc/kepler/Systems/1/NetworkAdapters/NetworkAdapter_1_0001010104')
    self.object_manage.mc:before_add_object(c_network_port.__class_name, mds_network_port)
    self.object_manage.mc:add_object(c_network_port.__class_name, mds_network_port, self.position)

    -- 自发现添加光模块对象
    local mds_opt = create_mds_object('OpticalModule_' .. (id + 1), {Name = 'op' .. id}, mds_network_port,
        '/bmc/kepler/Systems/1/NetworkAdapters/NetworkAdapter_1_0001010104/NetworkPort_' .. (id + 1) .. '0001010104')
    self.object_manage.mc:before_add_object(c_optical_module.__class_name, mds_opt)
    self.object_manage.mc:add_object(c_optical_module.__class_name, mds_opt, self.position)
end

function TEST_oem_smbus_network_adapter:InitRefChip(adapter)
    adapter.oem_smbus_config_obj = hardware_config.oem_smbus:new(adapter.RefChip)
    adapter.oem_smbus_config_obj.chip = chip
    hardware_config.chip = chip
end

function TEST_oem_smbus_network_adapter:tearDown()
    c_object_manage.destroy()
    self.database.db:close()
    hook_tasks.unhook()
end

function TEST_oem_smbus_network_adapter:flush()
    c_tasks.get_instance():run_all_task()
    self.database.db:flush()
end

function TEST_oem_smbus_network_adapter:test_can_set_port_lldp()
    skynet.sleep(3000)
    local adapter = c_network_adapter.collection:find({Position = self.position})
    self:InitRefChip(adapter)
    local port0 = c_network_port.collection:find({UUID = self.uuid, PortID = 0})
    local port1 = c_network_port.collection:find({UUID = self.uuid, PortID = 1})
    local port2 = c_network_port.collection:find({UUID = self.uuid, PortID = 2})
    local port3 = c_network_port.collection:find({UUID = self.uuid, PortID = 3})

    -- 通过外部设置lldp，实际修改寄存器内部值，再次读取校验值是否发生变更
    adapter.oem_smbus_config_obj:SetPortLLDPEnable(port0, true)
    local lldp_set_byte = adapter.RefChip:Read(nil, 88, 1)
    local lldp_byte = string.byte(lldp_set_byte, 1)
    local lldp_num = tonumber(string.format("%d", lldp_byte))
    lu.assertEquals(lldp_num, 7)

    adapter.oem_smbus_config_obj:SetPortLLDPEnable(port1, false)
    lldp_set_byte = adapter.RefChip:Read(nil, 88, 1)
    lldp_byte = string.byte(lldp_set_byte, 1)
    lldp_num = tonumber(string.format("%d", lldp_byte))
    lu.assertEquals(lldp_num, 5)

    -- 测试熟悉监听逻辑，调用set_LLDP_capability而非SetPortLLDPEnable
    port2.oem_smbus_config_obj = adapter.oem_smbus_config_obj
    port2:set_LLDP_capability(true)
    lldp_set_byte = adapter.RefChip:Read(nil, 88, 1)
    lldp_byte = string.byte(lldp_set_byte, 1)
    lldp_num = tonumber(string.format("%d", lldp_byte))
    lu.assertEquals(lldp_num, 7)

    -- 测试属性监听逻辑，调用set_LLDP_capability而非SetPortLLDPEnable
    port3.oem_smbus_config_obj = adapter.oem_smbus_config_obj
    port3:set_LLDP_capability(true)
    lldp_set_byte = adapter.RefChip:Read(nil, 88, 1)
    lldp_byte = string.byte(lldp_set_byte, 1)
    lldp_num = tonumber(string.format("%d", lldp_byte))
    lu.assertEquals(lldp_num, 15)
end

function TEST_oem_smbus_network_adapter:test_can_get_network_adapter_info_power_on()
    skynet.sleep(3000)
    local adapter = c_network_adapter.collection:find({Position = self.position})
    self:InitRefChip(adapter)

    local pro = {}
    adapter:update_oem_smbus_network_adapter_power_on(pro)

    lu.assertEquals(adapter.Position, self.position)
    lu.assertEquals(adapter.TemperatureCelsius, 47)
    lu.assertEquals(adapter.Health, 1)
end

function TEST_oem_smbus_network_adapter:test_can_get_network_adapter_info_power_off()
    skynet.sleep(3000)
    local adapter = c_network_adapter.collection:find({Position = self.position})
    adapter.TemperatureCelsius = 66
    adapter.Health = 1

    adapter:update_oem_smbus_info_power_off()

    lu.assertEquals(adapter.Position, self.position)
    lu.assertEquals(adapter.TemperatureCelsius, 0x4000)
    lu.assertEquals(adapter.Health, 0)
end

function TEST_oem_smbus_network_adapter:test_can_get_ports_info_power_on()
    skynet.sleep(3000)
    local adapter = c_network_adapter.collection:find({Position = self.position})
    self:InitRefChip(adapter)
    local ports = c_network_port.collection:fetch_by_position(self.position)
    adapter:update_oem_smbus_port_properties_power_on(ports)

    local port0 = c_network_port.collection:find({UUID = self.uuid, PortID = 0})
    lu.assertEquals(port0.MACAddress, '50:D3:3B:01:7D:0D')
    lu.assertEquals(port0.PermanentMACAddress, '50:D3:3B:01:7D:0D')
    lu.assertEquals(port0.FirmwareVersion, '3.0.6')
    lu.assertEquals(port0.LLDPEnabled, true)
    lu.assertEquals(port0.SpeedMbps, 25000)
    lu.assertEquals(port0.FullDuplex, true)
    lu.assertEquals(port0.AutoSpeedNegotiation, false)
    lu.assertEquals(port0.LinkStatus, 'Connected')
    local op0 = c_optical_module.collection:find({Name = 'op0'})
    -- 模拟上报无效数据，温度为10，不参与最值计算
    lu.assertEquals(op0.ReadingCelsius, 10)

    local port1 = c_network_port.collection:find({UUID = self.uuid, PortID = 1})
    lu.assertEquals(port1.MACAddress, '50:D3:3B:01:7D:0E')
    lu.assertEquals(port1.PermanentMACAddress, '50:D3:3B:01:7D:0E')
    lu.assertEquals(port1.FirmwareVersion, '3.0.6')
    lu.assertEquals(port1.LLDPEnabled, true)
    -- 模拟上报无效数据，速率为0
    lu.assertEquals(port1.SpeedMbps, 0)
    lu.assertEquals(port1.FullDuplex, true)
    lu.assertEquals(port1.AutoSpeedNegotiation, false)
    -- 模拟上报无效数据，网口状态为N/A
    lu.assertEquals(port1.LinkStatus, 'N/A')
    local op1 = c_optical_module.collection:find({Name = 'op1'})
    -- 模拟上报无效数据，温度为10，不参与最值计算
    lu.assertEquals(op1.ReadingCelsius, 10)

    local port2 = c_network_port.collection:find({UUID = self.uuid, PortID = 2})
    lu.assertEquals(port2.MACAddress, '50:D3:3B:01:7D:0F')
    lu.assertEquals(port2.PermanentMACAddress, '50:D3:3B:01:7D:0F')
    lu.assertEquals(port2.FirmwareVersion, '3.0.6')
    lu.assertEquals(port2.LLDPEnabled, true)
    lu.assertEquals(port2.SpeedMbps, 25000)
    lu.assertEquals(port2.FullDuplex, true)
    lu.assertEquals(port2.AutoSpeedNegotiation, false)
    lu.assertEquals(port2.LinkStatus, 'DisConnected')
    local op2 = c_optical_module.collection:find({Name = 'op2'})
    lu.assertEquals(op2.ReadingCelsius, 39)

    local port3 = c_network_port.collection:find({UUID = self.uuid, PortID = 3})
    lu.assertEquals(port3.MACAddress, '50:D3:3B:01:7D:10')
    lu.assertEquals(port3.PermanentMACAddress, '50:D3:3B:01:7D:10')
    lu.assertEquals(port3.FirmwareVersion, '3.0.6')
    lu.assertEquals(port3.LLDPEnabled, false)
    lu.assertEquals(port3.SpeedMbps, 25000)
    lu.assertEquals(port3.FullDuplex, true)
    lu.assertEquals(port3.AutoSpeedNegotiation, false)
    lu.assertEquals(port3.LinkStatus, 'DisConnected')
    local op3 = c_optical_module.collection:find({Name = 'op3'})
    lu.assertEquals(op3.ReadingCelsius, 38)

end

function TEST_oem_smbus_network_adapter:test_can_get_ports_info_power_off()
    skynet.sleep(3000)
    local port0 = c_network_port.collection:find({UUID = self.uuid, PortID = 0})
    port0.MACAddress = '50:D3:3B:01:7D:0D'
    port0.PermanentMACAddress = '50:D3:3B:01:7D:0D'
    port0.FirmwareVersion = '3.0.6'
    port0.LLDPEnabled = true
    port0.SpeedMbps = 25000
    port0.FullDuplex = true
    port0.AutoSpeedNegotiation = true
    port0.LinkStatus = 'Connected'
    local op0 = c_optical_module.collection:find({Name = 'op0'})
    op0.ReadingCelsius = 38

    local adapter = c_network_adapter.collection:find({Position = self.position})

    local ports = c_network_port.collection:fetch_by_position(self.position)
    adapter:update_oem_smbus_info_power_off(ports)
    -- mac,固件版本，lldp维持最后一次获取的数据不变，其余信息恢复
    lu.assertEquals(port0.MACAddress, '50:D3:3B:01:7D:0D')
    lu.assertEquals(port0.PermanentMACAddress, '50:D3:3B:01:7D:0D')
    lu.assertEquals(port0.FirmwareVersion, '3.0.6')
    lu.assertEquals(port0.LLDPEnabled, true)
    lu.assertEquals(port0.SpeedMbps, 0)
    lu.assertEquals(port0.FullDuplex, false)
    lu.assertEquals(port0.AutoSpeedNegotiation, false)
    lu.assertEquals(port0.LinkStatus, 'N/A')
    lu.assertEquals(op0.ReadingCelsius, 0xffff)
end